package structure;

import java.util.ArrayList;

import algorithm.miscellaneous;

public class powerset {

	private ArrayList<state> states;
	private ArrayList<String[]> connection;
	private graph graph;
	private enumerateGrammar e;
	
	public powerset(){
		
		this.states= null;
		this.connection= null;
		this.graph= null;
	}
	
	public powerset(enumerateGrammar e){
		
		this.states= new ArrayList<state>();
		this.connection= new ArrayList<String[]>();
		this.graph= new graph(e);
		this.e= e;
		
		this.createPowerset(e);
		
		this.connect();
	}
	
	//get
	//prendo tutti i collegamenti epsilon che l'item passato ha con gli altri
	private ArrayList<item> getInterBPconnection(item item, ArrayList<item> alreadyChecked){
		
		if(alreadyChecked == null) alreadyChecked= new ArrayList<item>();
		ArrayList<item> toReturn= new ArrayList<item>();
		ArrayList<item[]> connectionInterBP= this.graph.getConnectionInterBP();
		
		for(item[] i : connectionInterBP){
			
			if(i[0].sameAs(item)){
				
				alreadyChecked.add(i[0]);
				toReturn.add(i[1]);
				
				miscellaneous.addAllItemOnce(this.getInterBPconnection(i[1], alreadyChecked ), toReturn);
			}
		}
		
		return toReturn;
	}
	
	//prendo tutti i collegamenti interni alle big production
	private ArrayList<item> getIntraBPconnection(item item){
		
		ArrayList<item> toReturn= new ArrayList<item>();
		
		for(item[] i : this.graph.getConnectionIntraBP()){
			
			if(i[0].sameAs(item)){
				
				boolean toAdd= true;
				for(item ii : toReturn){
					if(ii.sameAs(i[1])){
						
						toAdd= false;
					}
				}
				if(toAdd)
					toReturn.add(i[1]);
				
			}
		}
		return toReturn;
	}

	public String getLabelByItems(ArrayList<item> items){
		
		String toReturn= "";
		for(state st: this.states){
			
			if(st.sameItem(items)){
				
				toReturn= st.getLabel();
			}
		}
		
		return toReturn;
	}
	
	public ArrayList<String[]> getConnection(){
		
		return this.connection;
	}
	
	public ArrayList<state> getStates(){
		
		return this.states;
	}
	
	public enumerateGrammar getEnumGrammar(){
		
		return this.e;
	}
	//
	
	//set
	
	//
	private void createPowerset(enumerateGrammar e){
		
		ArrayList<item> tmpItems= this.graph.getItems();
		ArrayList<item> tmpI= new ArrayList<item>();
		
		item i= tmpItems.get(0);
		ArrayList<String> lookahead=new ArrayList<String>();
		lookahead.add("$");
		//imposto label e controlli sul tipo di state
		int ID= 0;
		String label= "q0";
		boolean isFinal= false;		
		boolean isStart= true;
		
		//aggiungo il primo item allo stato iniziale
		tmpI.add(i);	
		
		//aggiungo tutti i collegamenti epsilon che l'item appenna inserito ha con il resto
		tmpI.addAll(this.getInterBPconnection(i, null));
		//miscellaneous.addAllItemOnce(this.getInterBPconnection(i, null), tmpI);
		state tmpState= new state(label, tmpI, isFinal, isStart);
		isStart= false;
		
		this.states.add(tmpState);	

		//this.printItAll();
		boolean stop= false;
		//ciclo sui nuovi stati
		while(!stop){
			boolean modified= false;
			ArrayList<state> toAdd= new ArrayList<state>();
			
			for(int j=0; j<this.states.size(); j++){
				
				state state= this.states.get(j);
				tmpI= new ArrayList<item>();
				tmpI= state.getItems();
				ArrayList<item> tmpII= new ArrayList<item>();
				
				
				//aggiungo tutti i collegamenti epsilon con altri item
				for(item ii : tmpI){
					
					miscellaneous.addAllItemOnce(this.getInterBPconnection(ii, null), tmpII);
				}
				miscellaneous.addAllItemOnce(tmpII, tmpI);
				for(item ii : tmpI){
					
					/*tmpII= new ArrayList<item>();
					tmpII.addAll(this.getIntraBPconnection(ii));
					
					ArrayList<item> tmpIII= new ArrayList<item>();
					
					for(item iii : tmpII){
						tmpIII.addAll(this.getInterBPconnection(iii));
					}
					
					tmpII.addAll(tmpIII);*/
					tmpII= new ArrayList<item>();

					tmpII= this.createState(ii);
					
					if(tmpII.size() == 1 && tmpII.get(0).isComplete() &&
							tmpII.get(0).getLeftside().equals(i.getLeftside()) &&
							tmpII.get(0).sameLookahead(i.getLookahead()))
						isFinal= true;
					else
						isFinal= false;

					//aggiungo un nuovo stato
					if(!this.isAlreadyIn(tmpII) && !tmpII.isEmpty()){
						ID++;
						label= "q"+ID;
						tmpState= new state(label, tmpII, isFinal, isStart);
						toAdd.add(tmpState);	
						modified= true;
					}
				}
				
				
			}
			this.states.addAll(toAdd);
			
			if(modified == false)
				stop= true;
		}
		
	}
	
	private ArrayList<item> createState(item tmpI){

		
		ArrayList<item> toReturn= new ArrayList<item>();
		
		//toReturn.addAll(this.getIntraBPconnection(tmpI));
		//miscellaneous.addAllItemOnce(this.getIntraBPconnection(tmpI),toReturn);
		toReturn.addAll(this.getIntraBPconnection(tmpI));
		System.out.print("tmpI= "); tmpI.printItAll();
		/*if(!tmpI.isComplete()){

			//miscellaneous.addAllItemOnce(this.getIntraBPconnection(tmpI),toReturn);
			
			ArrayList<item> tmpIII= new ArrayList<item>();
			
			for(item iii : toReturn){
				//tmpIII.addAll(this.getInterBPconnection(iii, null));
				miscellaneous.addAllItemOnce(this.getInterBPconnection(iii, null),tmpIII);
				System.out.println("tmpIII= ");
				for(item t : tmpIII) t.printItAll();
			}
			
			//toReturn.addAll(tmpIII);
			miscellaneous.addAllItemOnce(tmpIII,toReturn);
		}
		else
			toReturn.add(tmpI);*/
		

		ArrayList<item> tmpIII= new ArrayList<item>();
		
		for(item iii : toReturn){
			//tmpIII.addAll(this.getInterBPconnection(iii, null));
			miscellaneous.addAllItemOnce(this.getInterBPconnection(iii, null),tmpIII);
			System.out.println("tmpIII= ");
			for(item t : tmpIII) t.printItAll();
		}
		
		//toReturn.addAll(tmpIII);
		miscellaneous.addAllItemOnce(tmpIII,toReturn);

		System.out.println("toReturn= ");
		for(item ii : toReturn) ii.printItAll();
		System.out.println("");
	
		return toReturn;
	}
	
	//collega tra loro tutti gli stati
	private void connect(){
		
		for(int j=0; j<this.states.size(); j++){
			
			state state= this.states.get(j);
			ArrayList<item> tmpI= new ArrayList<item>();
			tmpI= state.getItems();
			ArrayList<item> tmpII= new ArrayList<item>();
			
			//scorro gli item nello stato
			for(item ii : tmpI){
				
				tmpII= this.createState(ii);
				
				for(int k=0; k<this.states.size(); k++){
					
					state tmpState= this.states.get(k);
					
					if(tmpState.sameItem(tmpII)){
						
						String[] conn= new String[3];
						conn[0]= state.getLabel();
						conn[1]= ii.getAfterDot();
						conn[2]= tmpState.getLabel();
						
						this.connection.add(conn);
					}
				}
			}
		}
	}
	
	
	
	//controlla se lo stato è stato gia inserito
	public boolean isAlreadyIn(ArrayList<item> newItems){
		
		boolean[] stateThere= new boolean[this.states.size()];
		for(int k=0; k<this.states.size(); k++){
			state st= this.states.get(k);
			//stessa dimensione
			if(st.getItems().size() == newItems.size()){
				//controllo che i sia uguale ad almeno un elemento di ii
				boolean[] there= new boolean[st.getItems().size()];
				for(int j=0; j<st.getItems().size(); j++){
					item i= st.getItems().get(j);
					boolean isThere= false;
					for(item ii : newItems){
						//ne ho trovato  uno uguale
						System.out.println("ii= "); ii.printItAll();
						if(i.sameAs(ii)){
							isThere= true;
						}
					}
					there[j]= isThere;
				}
				
				boolean innerThere= true;
				for(boolean b : there){
					
					if(b == false) innerThere=false;
				}
				
				if(innerThere){
					stateThere[k]= true;
				}
				else{
					stateThere[k]= false;
				}
				
			}
			else{
				stateThere[k]= false;
			}
		}
		
		for(boolean b : stateThere){
			
			if(b == true) return true;
		}
		
		return false;
	}
	
	public void printItAll(){
		
		this.graph.printItAll();
		
		
		for(state t : this.states){
			
			t.printItAll();
		}
		
		for(String[] s : this.connection){

			System.out.println(s[0] + " -->" + s[1] + " " + s[2]);			
		}
	}
}
